package com.baoyz.widget;

import java.security.InvalidParameterException;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Handler;
import android.util.TypedValue;

/**
 * Created by baoyz on 14/10/31.
 */
class CirclesDrawable extends RefreshDrawable implements Runnable {

	private static final float MAX_LEVEL = 10000;
	private static final float CIRCLE_COUNT = ProgressStates.values().length;
	private static final float MAX_LEVEL_PER_CIRCLE = MAX_LEVEL / CIRCLE_COUNT;
	@SuppressWarnings("unused")
	private static final int ALPHA_OPAQUE = 255;

	private Paint mFstHalfPaint;
	private Paint mScndHalfPaint;
	private Paint mAbovePaint;
	private RectF mOval = new RectF();
	private int mDiameter;
	private Path mPath;
	private int mHalf;
	private ProgressStates mCurrentState;
	private int mControlPointMinimum;
	private int mControlPointMaximum;
	private int mAxisValue;
	private ColorFilter mColorFilter;
	private static int mColor1;
	private static int mColor2;
	private static int mColor3;
	private static int mColor4;
	private int fstColor, scndColor;
	private boolean goesBackward;
	private Handler mHandler = new Handler();
	private int mLevel;
	private boolean isRunning;
	private int mTop;
	private int mDrawWidth;
	private int mDrawHeight;
	private Rect mBounds;

	public CirclesDrawable(Context context, PullRefreshLayout layout) {
		super(context, layout);
	}

	@Override
	public void start() {
		mLevel = 2500;
		isRunning = true;
		mHandler.postDelayed(this, 10);
	}

	@Override
	public void stop() {
		isRunning = false;
		mHandler.removeCallbacks(this);
	}

	@Override
	public boolean isRunning() {
		return isRunning;
	}

	@Override
	public void setColorSchemeColors(int[] colorSchemeColors) {
		initCirclesProgress(colorSchemeColors);
	}

	@Override
	public void setPercent(float percent) {
		int level = (int) (2500 * percent);

		updateLevel(level);
	}

	private void updateLevel(int level) {
		int animationLevel = level == MAX_LEVEL ? 0 : level;

		int stateForLevel = (int) (animationLevel / MAX_LEVEL_PER_CIRCLE);
		mCurrentState = ProgressStates.values()[stateForLevel];

		resetColor(mCurrentState);
		int levelForCircle = (int) (animationLevel % MAX_LEVEL_PER_CIRCLE);

		boolean halfPassed;
		if (!goesBackward) {
			halfPassed = levelForCircle != (int) (animationLevel % (MAX_LEVEL_PER_CIRCLE / 2));
		} else {
			halfPassed = levelForCircle == (int) (animationLevel % (MAX_LEVEL_PER_CIRCLE / 2));
			levelForCircle = (int) (MAX_LEVEL_PER_CIRCLE - levelForCircle);
		}

		mFstHalfPaint.setColor(fstColor);
		mScndHalfPaint.setColor(scndColor);

		if (!halfPassed) {
			mAbovePaint.setColor(mScndHalfPaint.getColor());
		} else {
			mAbovePaint.setColor(mFstHalfPaint.getColor());
		}

		mAbovePaint
				.setAlpha(200 + (int) (55 * (levelForCircle / MAX_LEVEL_PER_CIRCLE)));

		mAxisValue = (int) (mControlPointMinimum + (mControlPointMaximum - mControlPointMinimum)
				* (levelForCircle / MAX_LEVEL_PER_CIRCLE));

	}

	@Override
	public void offsetTopAndBottom(int offset) {
		mTop += offset;
		invalidateSelf();
	}

	@Override
	public void run() {
		mLevel += 80;
		if (mLevel > MAX_LEVEL)
			mLevel = 0;
		if (isRunning) {
			mHandler.postDelayed(this, 20);
			updateLevel(mLevel);
			invalidateSelf();
		}
	}

	private enum ProgressStates {
		FOLDING_DOWN, FOLDING_LEFT, FOLDING_UP, FOLDING_RIGHT
	}

	private void initCirclesProgress(int[] colors) {
		initColors(colors);
		mPath = new Path();

		Paint basePaint = new Paint();
		basePaint.setAntiAlias(true);

		mFstHalfPaint = new Paint(basePaint);
		mScndHalfPaint = new Paint(basePaint);
		mAbovePaint = new Paint(basePaint);

		setColorFilter(mColorFilter);
	}

	private void initColors(int[] colors) {
		if (colors == null || colors.length < 4)
			throw new InvalidParameterException(
					"The color scheme length must be 4");
		mColor1 = colors[0];
		mColor2 = colors[1];
		mColor3 = colors[2];
		mColor4 = colors[3];
	}

	@Override
	protected void onBoundsChange(Rect bounds) {
		super.onBoundsChange(bounds);
		mDrawWidth = dp2px(40);
		mDrawHeight = mDrawWidth;
		mTop = -mDrawHeight
				- (getRefreshLayout().getFinalOffset() - mDrawHeight) / 2;
		mBounds = bounds;
		measureCircleProgress(mDrawWidth, mDrawHeight);
	}

	private void resetColor(ProgressStates currentState) {
		switch (currentState) {
		case FOLDING_DOWN:
			fstColor = mColor1;
			scndColor = mColor2;
			goesBackward = false;
			break;
		case FOLDING_LEFT:
			fstColor = mColor1;
			scndColor = mColor3;
			goesBackward = true;
			break;
		case FOLDING_UP:
			fstColor = mColor3;
			scndColor = mColor4;
			goesBackward = true;
			break;
		case FOLDING_RIGHT:
			fstColor = mColor2;
			scndColor = mColor4;
			goesBackward = false;
			break;
		}
	}

	@Override
	public void draw(Canvas canvas) {
		if (mCurrentState != null) {
			canvas.save();
			canvas.translate(mBounds.width() / 2 - mDrawWidth / 2, mTop);
			makeCirclesProgress(canvas);
			canvas.restore();
		}
	}

	private void measureCircleProgress(int width, int height) {
		mDiameter = Math.min(width, height);
		mHalf = mDiameter / 2;
		mOval.set(0, 0, mDiameter, mDiameter);
		mControlPointMinimum = -mDiameter / 6;
		mControlPointMaximum = mDiameter + mDiameter / 6;
	}

	private void makeCirclesProgress(Canvas canvas) {

		switch (mCurrentState) {
		case FOLDING_DOWN:
		case FOLDING_UP:
			drawYMotion(canvas);
			break;
		case FOLDING_RIGHT:
		case FOLDING_LEFT:
			drawXMotion(canvas);
			break;
		}

		canvas.drawPath(mPath, mAbovePaint);
	}

	private void drawXMotion(Canvas canvas) {
		canvas.drawArc(mOval, 90, 180, true, mFstHalfPaint);
		canvas.drawArc(mOval, -270, -180, true, mScndHalfPaint);
		mPath.reset();
		mPath.moveTo(mHalf, 0);
		mPath.cubicTo(mAxisValue, 0, mAxisValue, mDiameter, mHalf, mDiameter);
	}

	private void drawYMotion(Canvas canvas) {
		canvas.drawArc(mOval, 0, -180, true, mFstHalfPaint);
		canvas.drawArc(mOval, -180, -180, true, mScndHalfPaint);
		mPath.reset();
		mPath.moveTo(0, mHalf);
		mPath.cubicTo(0, mAxisValue, mDiameter, mAxisValue, mDiameter, mHalf);
	}

	@Override
	public void setColorFilter(ColorFilter cf) {
		this.mColorFilter = cf;
		mFstHalfPaint.setColorFilter(cf);
		mScndHalfPaint.setColorFilter(cf);
		mAbovePaint.setColorFilter(cf);
	}

	private int dp2px(int dp) {
		return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
				getContext().getResources().getDisplayMetrics());
	}
}
